use std::{
    collections::VecDeque,
    ffi::c_void,
    ptr::null_mut,
    sync::{
        Arc, Condvar, Mutex, RwLock
    },
    thread::{sleep, Builder}, time::Duration,
};

use crossbeam_channel::{bounded, Receiver, Sender, select};
use ffmpeg::{
    av_fifo_read_safe, av_frame_alloc_safe, av_frame_free_safe, av_packet_alloc_safe, av_packet_free_safe, av_packet_move_ref_safe, av_packet_unref_safe, av_q2d_safe, av_samples_get_buffer_size_safe, avcodec_alloc_context3_safe, avcodec_close_safe, avcodec_open2_safe, avcodec_parameters_to_context_safe, avcodec_receive_frame_safe, avcodec_send_packet_safe, swr_alloc_safe, swr_alloc_set_opts2_safe, swr_convert_safe, swr_free_safe, swr_init_safe, avcodec_find_decoder_safe, avcodec_flush_buffers_safe
};
use ffmpeg_sys_next::{
    AVChannelLayout, AVCodec, AVCodecContext, AVCodecParameters, AVERROR, AVERROR_EOF, AVFrame,
    AVRational, AVSampleFormat, EAGAIN, SwrContext, 
};
use sdl2::{
    Sdl,
    audio::{AudioCallback, AudioDevice, AudioSpecDesired},
};

use crate::{
    AudioDecoderData, CustomError, MediaStatus, Packet, PacketFifo, Timer, MAX_AUDIO_FRAME_SIZE, MAX_FRAME_SIZE, MIN_QUEUE_SIZE, CodecContextSafe, SwrContextSafe
};

#[derive(Debug, Clone)]
pub struct SquareWave {
    pub data: Arc<std::sync::Mutex<VecDeque<Vec<i16>>>>,
}

impl AudioCallback for SquareWave {
    type Channel = i16;

    fn callback(&mut self, out: &mut [i16]) {
        let mut buffer = self.data.lock().unwrap();
        match buffer.pop_front() {
            None => {}
            Some(data) => out.copy_from_slice(data.as_slice()),
        };
    }
}

pub struct SafeAudiaDevice {
    pub device: Arc<Option<AudioDevice<SquareWave>>>,
}

unsafe impl Sync for SafeAudiaDevice {}
unsafe impl Send for SafeAudiaDevice {}

pub struct AudioPlayer {
    pub index: isize,
    pub audio_codec_parameters: *mut AVCodecParameters,
    pub channels: i32,
    pub sample_rate: i32,
    pub sample_fmt: AVSampleFormat,
    pub out_channel_layout: *const AVChannelLayout,
    pub out_nb_samples: i32,
    pub out_sample_fmt: AVSampleFormat,
    pub out_sample_rate: i32,
    pub time_base: AVRational,
    pub out_buffer_size: i32,
    pub audio_codec: *mut AVCodec,
    pub av_codec_context: *mut AVCodecContext,
    pub swr_context: *mut SwrContext,
    pub audio_device: Arc<Option<AudioDevice<SquareWave>>>,
    pub audio_callback: Arc<std::sync::Mutex<VecDeque<Vec<i16>>>>,
    pub audio_time: Arc<RwLock<f64>>,
    pub sdl: Sdl,
    pub packet_fifo: Arc<Mutex<PacketFifo>>,
    pub condvar: Arc<Condvar>,
    pub receiver: Option<Arc<Mutex<Receiver<AudioDecoderData>>>>,
    pub timer: Arc<Mutex<Timer>>,
    pub media_status: Arc<MediaStatus>,
    pub seek_receiver: Receiver<i8>,
}

impl AudioPlayer {
    pub fn new(sdl: Sdl, packet_fifo: Arc<Mutex<PacketFifo>>, condvar: Arc<Condvar>, timer: Arc<Mutex<Timer>>, media_status: Arc<MediaStatus>, seek_receiver: Receiver<i8>) -> Self {
        AudioPlayer {
            index: -1,
            audio_codec_parameters: null_mut(),
            channels: 2,
            sample_rate: 44100,
            out_channel_layout: null_mut(),
            out_nb_samples: 1024,
            sample_fmt: ffmpeg_sys_next::AVSampleFormat::AV_SAMPLE_FMT_S16,
            out_sample_fmt: ffmpeg_sys_next::AVSampleFormat::AV_SAMPLE_FMT_S16,
            out_sample_rate: 44100,
            time_base: AVRational { num: 0, den: 0 },
            out_buffer_size: 0,
            audio_codec: null_mut(),
            av_codec_context: null_mut(),
            swr_context: null_mut(),
            audio_device: Arc::new(None),
            audio_callback: Arc::new(std::sync::Mutex::new(VecDeque::new())),
            audio_time: Arc::new(RwLock::new(0_f64)),
            sdl,
            packet_fifo,
            condvar,
            receiver: None,
            timer,
            media_status,
            seek_receiver,
        }
    }

    pub fn avcodec_alloc_context(mut self) -> Result<Self, CustomError> {
        let av_codec_context = avcodec_alloc_context3_safe(null_mut());
        if av_codec_context.is_null() {
            return Err(CustomError::FfmepgError("Unsupported codec".to_owned()));
        }
        self.av_codec_context = av_codec_context;
        Ok(self)
    }

    pub fn avcodec_parameters_to_context(self) -> Result<Self, CustomError> {
        if avcodec_parameters_to_context_safe(self.av_codec_context, self.audio_codec_parameters)
            != 0
        {
            return Err(CustomError::FfmepgError(
                "Couldn't copy codec context".to_owned(),
            ));
        }
        Ok(self)
    }

    pub fn avcodec_find_decoder(mut self) -> Result<Self, CustomError> {
        //根据音视频流对应的解码器上下文查找对应的解码器，返回对应的解码器(信息结构体)
        let av_codec = avcodec_find_decoder_safe(unsafe { (*self.av_codec_context).codec_id });
        if av_codec.is_null() {
            return Err(CustomError::FfmepgError("Unsupported codec".to_owned()));
        }
        self.audio_codec = av_codec as *mut AVCodec;
        Ok(self)
    }

    pub fn avcodec_open(mut self) -> Result<Self, CustomError> {
        if avcodec_open2_safe(self.av_codec_context, self.audio_codec, null_mut()) != 0 {
            return Err(CustomError::FfmepgError("Could not open codec.".to_owned()));
        }
        self.sample_fmt = unsafe { (*self.av_codec_context).sample_fmt };
        Ok(self)
    }

    pub fn swr_context(mut self) -> Result<Self, CustomError> {
        self.swr_context = swr_alloc_safe();
        swr_alloc_set_opts2_safe(
            &mut self.swr_context,
            self.out_channel_layout,
            self.out_sample_fmt,
            self.out_sample_rate,
            self.out_channel_layout,
            self.sample_fmt,
            self.sample_rate,
            0,
            null_mut(),
        );
        swr_init_safe(self.swr_context);
        Ok(self)
    }

    pub fn get_samples_buffer_size(mut self) -> Result<Self, CustomError> {
        self.out_buffer_size = av_samples_get_buffer_size_safe(
            null_mut(),
            unsafe { (*self.out_channel_layout).nb_channels },
            self.out_nb_samples,
            self.out_sample_fmt,
            1,
        );
        Ok(self)
    }

    pub fn init_audio_sdl(mut self) -> Result<Self, CustomError> {
        let desired_spec = AudioSpecDesired {
            freq: Some(self.out_sample_rate),
            channels: Some(self.channels as u8),
            samples: Some(1024),
        };

        let audio_subsystem = self.sdl.audio().unwrap();

        let device = audio_subsystem
            .open_playback(None, &desired_spec, |_spec| {
                // initialize the audio callback
                SquareWave {
                    data: self.audio_callback.clone(),
                }
            })
            .unwrap();
        self.audio_device = Arc::new(Some(device));
        Ok(self)
    }

    pub fn decode_packet(mut self) -> Result<Self, CustomError> {
        let packet_fifo = self.packet_fifo.clone();
        let condvar = self.condvar.clone();
        let codec_context = CodecContextSafe {
            av_codec_context: self.av_codec_context,
        };
        let out_buffer_size = self.out_buffer_size;
        let time_base = self.time_base;
        let swr_cxt = SwrContextSafe {
            swr_context: self.swr_context,
        };
        let (sender, receiver): (Sender<AudioDecoderData>, Receiver<AudioDecoderData>) = bounded(MAX_FRAME_SIZE);
        self.receiver = Some(Arc::new(Mutex::new(receiver)));
        let media_status = self.media_status.clone();


        Builder::new()
            .name("audio decode thread".to_string())
            .spawn(move || {
                let _ = Self::loop_decode_packet(
                    codec_context,
                    packet_fifo,
                    condvar,
                    out_buffer_size,
                    time_base,
                    swr_cxt,
                    sender,
                    media_status,
                );
            })
            .unwrap();
        Ok(self)
    }

    pub fn loop_decode_packet(
        codec_context: CodecContextSafe,
        packet_fifo: Arc<Mutex<PacketFifo>>,
        condvar: Arc<Condvar>,
        out_buffer_size: i32,
        time_base: AVRational,
        swr_cxt: SwrContextSafe,
        sender: Sender<AudioDecoderData>,
        media_status: Arc<MediaStatus>,
    ) -> Result<(), CustomError> {
        loop {
            media_status.check_pause()?;

            let packet = {
                let mut packet_fifo = packet_fifo.lock().unwrap();

                let mut src_packet = Packet::new();
                let res = av_fifo_read_safe((*packet_fifo).fifo, &mut src_packet as *mut Packet as *mut c_void, 1);
                if res < 0 {
                    let mut status_lock = media_status.status.lock().unwrap();
                    if (*status_lock).packet_end() {
                        (*status_lock).set_audio_frame_end();
                        return Ok(());
                    } else if (*status_lock).video_frame_end() {
                        (*status_lock).set_play_end();
                        return Ok(());
                    } else {
                        continue;
                    }
                }
                if packet_fifo.size() == 0 {
                    continue;
                }
                let curr_size = packet_fifo.size() - 1;
                packet_fifo.put_size(curr_size);
                if curr_size < MIN_QUEUE_SIZE {
                    condvar.notify_all();
                }
                let dst_packet = av_packet_alloc_safe();
                av_packet_move_ref_safe(dst_packet, src_packet.pkt);
                av_packet_free_safe(&mut src_packet.pkt);
                dst_packet
            };

            if avcodec_send_packet_safe(codec_context.av_codec_context, packet) < 0 {
                av_packet_unref_safe(packet);
                println!("Failed to decode packet.");
                return Err(CustomError::FfmepgError(
                    "Failed to decode packet".to_owned(),
                ));
            }
            av_packet_unref_safe(packet);

            loop {
                let mut decoder_data = AudioDecoderData::default();
                {
                    let mut frame = av_frame_alloc_safe();
                    let ret = avcodec_receive_frame_safe(codec_context.av_codec_context, frame);
                    if ret == AVERROR(EAGAIN) || ret == AVERROR_EOF {
                        av_frame_free_safe(&mut frame);
                        break;
                    } else if ret < 0 {
                        av_frame_free_safe(&mut frame);
                        return Err(CustomError::FfmepgError("Decoding error".to_owned()));
                    }

                    let size = (out_buffer_size * 2) as usize;
                    decoder_data.data.reserve_exact(size);
                    let dest = [decoder_data.data.as_mut_ptr()];
                    Self::swr_convert(frame, dest, swr_cxt.clone())?;

                    let audio_time = (unsafe { (*frame).pts }) as f64 * av_q2d_safe(time_base) * 1000_f64;
                    decoder_data.audio_time = audio_time;

                    av_frame_free_safe(&mut frame);
                }
                {
                    let _ = sender.send(decoder_data);
                }
            }
        }
        //Ok(())
    }

    pub fn swr_convert(
        frame: *mut AVFrame,
        dest: [*mut u8; 1],
        swr_cxt: SwrContextSafe,
    ) -> Result<(), CustomError> {
        let res = swr_convert_safe(
            swr_cxt.swr_context,
            dest.as_ptr() as *mut *mut u8,
            MAX_AUDIO_FRAME_SIZE,
            unsafe { (*frame).data.as_ptr() } as *mut *const u8,
            unsafe { (*frame).nb_samples },
        );
        if res < 0 {
            return Err(CustomError::FfmepgError("音频转码失败.".to_owned()));
        }
        Ok(())
    }

    pub fn player(self) -> Result<Self, CustomError> {
        match self.audio_device.as_ref() {
            Some(device) => {
                device.resume();
            }
            None => {
                println!("Audio resume failed");
            }
        };

        let out_buffer_size = self.out_buffer_size;
        let receiver = self.receiver.clone().unwrap();
        let audio_callback = self.audio_callback.clone();
        let timer = self.timer.clone();
        let media_status = self.media_status.clone();
        let audio_device = SafeAudiaDevice {device: self.audio_device.clone()};
        let seek_receiver = self.seek_receiver.clone();
        let packet_fifo = self.packet_fifo.clone();
        let codec_context = CodecContextSafe {av_codec_context: self.av_codec_context};
        let condvar = self.condvar.clone();
        Builder::new()
            .name("audio device thread".to_string())
            .spawn(move || {
                Self::loop_player(
                    out_buffer_size, 
                    receiver, 
                    audio_callback, 
                    timer, 
                    media_status, 
                    audio_device, 
                    seek_receiver, 
                    packet_fifo, 
                    codec_context,
                    condvar,
                ).unwrap();
            })
            .unwrap();

        Ok(self)
    }

    pub fn loop_player(
        out_buffer_size: i32,
        receiver: Arc<Mutex<Receiver<AudioDecoderData>>>,
        audio_callback: Arc<std::sync::Mutex<VecDeque<Vec<i16>>>>,
        timer: Arc<Mutex<Timer>>,
        media_status: Arc<MediaStatus>,
        audio_device: SafeAudiaDevice,
        seek_receiver: Receiver<i8>,
        packet_fifo: Arc<Mutex<PacketFifo>>,
        codec_context: CodecContextSafe,
        condvar: Arc<Condvar>,
    ) -> Result<(), CustomError> {
        loop {
            select! {
                recv(seek_receiver) -> _seek => {
                    std::thread::sleep(std::time::Duration::from_millis(100));
                    avcodec_flush_buffers_safe(codec_context.av_codec_context);
                    
                    {
                        let mut packet_fifo = packet_fifo.lock().unwrap();
                        loop {
                            let mut src_packet = Packet::new();
                            let res = av_fifo_read_safe((*packet_fifo).fifo, &mut src_packet as *mut Packet as *mut c_void, 1);
                            //println!("audio res: {}", res);
                            av_packet_free_safe(&mut src_packet.pkt);
                            if res < 0 {
                                packet_fifo.put_size(0);
                                println!("audio fifo break;");
                                break;
                            }
                        }
                    }
                    
                    let receiver_lock = receiver.lock().unwrap();
                    loop {
                        match receiver_lock.try_recv() {
                            Ok(_) => {},
                            _ => {
                                println!("audio receiver break;");
                                //condvar.notify_all();
                                break;
                            },
                        }
                    }
                },
                default => {

                },
            }

            if let Some(device) = audio_device.device.as_ref() {
                media_status.audio_check_status(device)?;
            }

            let audio_data = {
                let receiver_lock = receiver.lock().unwrap();
                match receiver_lock.recv() {
                    Ok(data) => data,
                    _ => continue,
                }
            };

            let mut result: Vec<i16> = Vec::new();
            unsafe {
                for i in 0..(out_buffer_size / 2) {
                    result.push(
                        (((*(audio_data.data.as_ptr()).add((i * 2 + 1) as usize)) as i16) << 8)
                            | *(audio_data.data.as_ptr()).add((i * 2) as usize) as i16,
                    );
                }
            }
            {
                audio_callback.lock().unwrap().push_back(result);
            }

            let run_time = {
                timer.lock().unwrap().total_elapsed().as_millis() as i64
            };
            let sleep_time = audio_data.audio_time as i64 - run_time;
            //println!("audio run_time: {}, time: {}, audio time: {}", run_time, audio_data.audio_time, sleep_time);
            
            if sleep_time > 0 {
                sleep(Duration::from_millis(sleep_time as u64));
            }
        }
    }

    pub fn close(mut self) -> Result<Self, CustomError> {
        swr_free_safe(&mut self.swr_context);
        avcodec_close_safe(self.av_codec_context);
        Ok(self)
    }
}
